技巧22 清除缓存
使用 --no-cache
标志通常足以解决在缓存方面遇到的任何问题。但是有时候往往需要的是一个更细粒度的解决方案。如果有一个构建需要花费很长时间,例如,用户可能想在某个点之前使用缓存,之后使其失效以便重新执行一条命令并创建一个新镜像。
问题
想要在Dockerfile构建中从一个指定的点开始让Docker构建缓存失效。
解决方案
在命令后面增加一条无害的注释,从而让缓存失效。
不妨从https://github.com/docker-in-practice/todo的Dockerfile开始上手(它对应下列输出里的每个 Step
行),我们已经完成了一次构建,并随后在 CMD
这一行上添加了一条注释。我们可以在这里看到再次执行 docker build
的输出:
$ docker build . ⇽--- 一次“正常”的docker构建
Sending build context to Docker daemon 2.56 kB
Sending build context to Docker daemon
Step 0 : FROM node
---> 91cbcf796c2c
Step 1 : MAINTAINER ian.miell@gmail.com
---> Using cache
---> 8f5a8a3d9240
Step 2 : RUN git clone -q https://github.com/docker-in-practice/todo.git
---> Using cache
---> 48db97331aa2
Step 3 : WORKDIR todo
---> Using cache
---> c5c85db751d6
Step 4 : RUN npm install
---> Using cache
---> be943c45c55b
Step 5 : EXPOSE 8000
---> Using cache ⇽--- 到这里为止均是使用的缓存
---> 805b18d28a65
Step 6 : CMD ["npm","start"] #bust the cache ⇽--- 缓存不再有效了,但是该命令实际上没有变化
---> Running in fc6c4cd487ce
---> d66d9572115e ⇽--- 一个新的镜像被创建
Removing intermediate container fc6c4cd487ce
Successfully built d66d9572115e
本技巧的原理在于Docker将非空的更改均当作一行新的命令来对待,因此缓存的镜像层便不会再被复用。
读者可能会感到疑惑(我们第一次看到Docker时也有同感),Docker的层是否可以在镜像之间迁移并且合并它们,就像它们是Git中的更改集一样。在Docker里,至少当前是不可能实现的。一个层被定义成仅仅是一个指定镜像的更改集。因此,一旦缓存被破坏了,构建里的命令就不能再复用它。
正因为如此,如果可以,建议最好将不太可能变动的命令放到更靠近Dockerfile开头的位置。
讨论
最初迭代一份Dockerfile时,把每一行单独的命令拆分到一个单独的层有助于加快迭代速度,因为用户可以选择性地重新运行部分流程,如上述代码中展示的那样,但是这并不太适用于产出一个精简的最终镜像的场景。在复杂度足够高的情况下构建镜像时,层的数量达到硬性限制的42个并非闻所未闻。为了缓解这一情况,一旦用户得到了一个满意的正常工作的构建结果,就应该看看技巧56里介绍的步骤,去创建一个生产就绪的镜像。